package com.sinszm.wx.pay.util;

import com.sinszm.common.exception.ApiException;
import com.sinszm.common.util.SignUtils;
import com.sinszm.common.util.XmlUtils;
import org.nutz.http.Http;
import org.nutz.http.Request;
import org.nutz.http.Response;
import org.nutz.http.Sender;
import org.springframework.util.StringUtils;

import javax.net.ssl.*;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.security.cert.X509Certificate;
import java.util.SortedMap;

import static com.sinszm.wx.exception.WxApiError.*;

/**
 * 微信支付请求工具
 * <p>
 *     默认的请求超时限定8秒
 * </p>
 * @author chenjianbo
 */
final class PayRequest {

    /**
     * 微信支付带证书请求
     * @param url           请求地址
     * @param xml           请求参数体
     * @param mchId         商户ID
     * @param inputStream   证书流
     * @param certKey       签名密钥
     * @param method        签名方式
     */
    synchronized static SortedMap<String, String> postBySSL(String url, String xml, String mchId, InputStream
            inputStream, String certKey, SignUtils.Method method){
        return postBySSL(url, xml, mchId, inputStream, certKey, method, false);
    }

    /**
     * 微信支付带证书请求
     * @param url           请求地址
     * @param xml           请求参数体
     * @param mchId         商户ID
     * @param inputStream   证书流
     * @param certKey       签名密钥
     * @param method        签名方式
     * @param noSign        是否签名：true不签名，false需要签名
     */
    synchronized static SortedMap<String, String> postBySSL(String url, String xml, String mchId, InputStream
            inputStream, String certKey, SignUtils.Method method, boolean noSign) {
        if (StringUtils.isEmpty(url)) {
            throw new ApiException(WX_PAY_ERROR_008);
        }
        if (StringUtils.isEmpty(xml)) {
            throw new ApiException(WX_PAY_ERROR_009);
        }
        if (StringUtils.isEmpty(mchId)) {
            throw new ApiException(WX_PAY_ERROR_010);
        }
        if (inputStream == null) {
            throw new ApiException(WX_PAY_ERROR_011);
        }
        if (StringUtils.isEmpty(certKey)) {
            throw new ApiException(WX_PAY_ERROR_014);
        }
        if (method == null) {
            throw new ApiException(WX_PAY_ERROR_015);
        }
        Request req = Request.create(url, Request.METHOD.POST);
        SSLSocketFactory sslSocketFactory;
        try {
            //证书装载
            KeyStore keyStore  = KeyStore.getInstance("PKCS12");
            keyStore.load(inputStream, mchId.toCharArray());
            KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
            kmf.init(keyStore, mchId.toCharArray());
            TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return new X509Certificate[] {};
                }
                @Override
                public void checkClientTrusted(X509Certificate[] chain, String authType) {
                }
                @Override
                public void checkServerTrusted(X509Certificate[] chain, String authType) {
                }
            } };
            SSLContext sslContext = SSLContext.getInstance("TLSv1");
            sslContext.init(kmf.getKeyManagers(), trustAllCerts, new SecureRandom());
            sslSocketFactory = sslContext.getSocketFactory();
            //请求处理
            req.setData(xml);
            req.getHeader().set("Content-Type", "application/xml");
        } catch (Exception e) {
            e.printStackTrace(System.err);
            throw new ApiException(WX_PAY_ERROR_005);
        }
        if (sslSocketFactory == null) {
            throw new ApiException(WX_PAY_ERROR_007);
        }
        Response response = Sender.create(req).setSSLSocketFactory(sslSocketFactory).setTimeout(8000).send();
        if (response.isOK()) {
            String content = response.getContent();
            if (StringUtils.isEmpty(content)
                    || !content.contains("<xml>")) {
                content = "<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA["
                        + (StringUtils.isEmpty(content) ? "0" : content)
                        + "]]></return_msg></xml>";
            }
            return transform(content, certKey, method, noSign);
        }
        throw new ApiException(WX_PAY_ERROR_006);
    }

    /**
     * 微信支付请求
     * @param url           请求地址
     * @param xml           请求参数体
     * @param certKey       签名密钥
     * @param method        签名方式
     */
    synchronized static SortedMap<String, String> post(String url, String xml, String certKey, SignUtils.Method method) {
        return post(url, xml, certKey, method, false);
    }

    /**
     * 微信支付请求
     * @param url           请求地址
     * @param xml           请求参数体
     * @param certKey       签名密钥
     * @param method        签名方式
     * @param noSign        是否签名：true不签名，false需要签名
     */
    synchronized static SortedMap<String, String> post(String url, String xml, String certKey, SignUtils.Method
            method, boolean noSign) {
        if (StringUtils.isEmpty(url)) {
            throw new ApiException(WX_PAY_ERROR_008);
        }
        if (StringUtils.isEmpty(xml)) {
            throw new ApiException(WX_PAY_ERROR_009);
        }
        if (StringUtils.isEmpty(certKey)) {
            throw new ApiException(WX_PAY_ERROR_014);
        }
        if (method == null) {
            throw new ApiException(WX_PAY_ERROR_015);
        }
        Response response = Http.postXML(url, xml,8000);
        if (response.isOK()) {
            return transform(response.getContent(), certKey, method, noSign);
        }
        throw new ApiException(WX_PAY_ERROR_006);
    }

    /**
     * 转换处理返回值
     * @param content
     * @param certKey
     * @param method
     */
    synchronized static SortedMap<String, String> transform(String content, String certKey, SignUtils.Method method,
                                                            boolean noSign) {
        SortedMap<String, String> map = XmlUtils.basicXmlToMap(content);
        final String key = "sign";
        if (!noSign && !map.containsKey(key)) {
            return map;
        }
        //取得原始签名串
        String sourceSign = map.get("sign");
        map.remove("sign");
        //创建新签名
        String targetSign = SignUtils.createSign(map, certKey, method);
        if (!noSign) {
            if (StringUtils.isEmpty(sourceSign) || !sourceSign.equals(targetSign)) {
                throw new ApiException(WX_PAY_ERROR_012);
            }
        }
        map.put("sign",targetSign);
        return map;
    }

}
